﻿using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using QuickPrompt.DomainModel;
using QuickPrompt.DomainModel.Identity;
using QuickPrompt.Test.UnitTests.Fake;
using RedandBlue.Common.Authorization;
using RedandBlue.RepositoryPattern.DataContext;
using RedandBlue.RepositoryPattern.EF6;
using RedandBlue.RepositoryPattern.Infrastructure;
using RedandBlue.RepositoryPattern.UnitOfWork;

namespace QuickPrompt.Test.UnitTests.Repository
{
    [TestFixture]
    class UserRepository
    {
        [SetUp]
        public void Init()
        {

        }

        [TearDown]
        public void Cleanup()
        {
            // todo: delete QuickPromptTemp.mfd (LocalDb)
            // cleanup all the infrastructure that was needed for our tests.
        }

        [Test(Description = "Delete User by UserId")]
        [TestCase(2)]
        public void Delete(int userId)
        {
            using (IDataContextAsync quickPromptFakeContext = new QuickPromptFakeContext())
            using (IUnitOfWorkAsync unitOfWork = new UnitOfWork(quickPromptFakeContext))
            {
                unitOfWork.Repository<User>().Insert(new User
                {
                    //UserId = userId,
                    IsDeleted = false,
                    IsActive = true,
                    ObjectState = ObjectState.Added
                });
                unitOfWork.SaveChanges();
                unitOfWork.Repository<User>().Delete(userId);
                unitOfWork.SaveChanges();
                var product = unitOfWork.Repository<User>().Find(userId);
                Assert.IsNull(product);
            }
        }


        [Test(Description = "Delete User by User Object")]
        [TestCase(2, true)]
        public void Delete(int userId, bool isActive)
        {
            using (IDataContextAsync quickPromptFakeContext = new QuickPromptFakeContext())
            using (IUnitOfWorkAsync unitOfWork = new UnitOfWork(quickPromptFakeContext))
            {
                unitOfWork.Repository<User>().Insert(new User { IsActive = isActive, ObjectState = ObjectState.Added });
                unitOfWork.SaveChanges();
                var user = unitOfWork.Repository<User>().Find(2);
                user.ObjectState = ObjectState.Deleted;
                unitOfWork.Repository<User>().Delete(user);
                unitOfWork.SaveChanges();
                var userDeleted = unitOfWork.Repository<User>().Find(2);
                Assert.IsNull(userDeleted);
            }
        }

        [Test(Description = "Deep Load User with Reminders")]
        [TestCase(1, 1, "Reminder1")]
        public void DeepLoad(string userName, int reminderId, string reminderName)
        {
            using (IDataContextAsync quickPromptFakeContext = new QuickPromptFakeContext())
            using (IUnitOfWorkAsync unitOfWork = new UnitOfWork(quickPromptFakeContext))
            {
                var user = unitOfWork.Repository<User>()
                   .Query(x => x.UserName.Equals(userName, StringComparison.OrdinalIgnoreCase))
                   .Select()
                   .FirstOrDefault();
                Assert.IsNotNull(user);
                //unitOfWork.Repository<User>().Insert(new User
                //{
                //    UserId = userId,
                //    ObjectState = ObjectState.Added
                //});
                unitOfWork.Repository<QuickNote>().Insert(
                    new QuickNote
                    {
                        Id = reminderId,
                        Name = reminderName,
                        UserGuid = user.Id,
                        ObjectState = ObjectState.Added
                    });
                unitOfWork.SaveChanges();
                var userEntity = unitOfWork.Repository<User>().Find(user.Id);
                Assert.IsNotNull(userEntity);
            }
        }


        [Test(Description = "Find User by UserId")]
        [TestCase("habo")]
        public void Find(string username)
        {
            using (IDataContextAsync quickPromptFakeContext = new QuickPromptFakeContext())
            using (IUnitOfWorkAsync unitOfWork = new UnitOfWork(quickPromptFakeContext))
            {
                unitOfWork.Repository<User>().Insert(new User { UserName = username,  IsActive = false, ObjectState = ObjectState.Added });
                unitOfWork.Repository<User>().Insert(new User { IsActive = true, ObjectState = ObjectState.Added });
                unitOfWork.Repository<User>().Insert(new User { IsActive = true, ObjectState = ObjectState.Added });
                unitOfWork.SaveChanges();
                var user = unitOfWork.Repository<User>().Query(x=>x.UserName == username).Select().FirstOrDefault();
                Assert.IsNotNull(user);
                Assert.AreEqual(username, user.UserName);
            }
        }

        [Test(Description = "Get Users")]
        public void GetUsers()
        {
            using (IDataContextAsync quickPromptFakeContext = new QuickPromptFakeContext())
            using (IUnitOfWorkAsync unitOfWork = new UnitOfWork(quickPromptFakeContext))
            {
                var users = unitOfWork.Repository<User>().Query().Select().ToList();
                Assert.IsInstanceOf<List<User>>(users);
            }
        }


        [Test(Description = "Insert User")]
        [TestCase("hbopuri", "test1", "test2", "test3")]
        public void AddUsers(string username, string pwd1, string pwd2, string pwd3)
        {
            Password password = new Password();
            using (IDataContextAsync quickPromptFakeContext = new QuickPromptFakeContext())
            using (IUnitOfWorkAsync unitOfWork = new UnitOfWork(quickPromptFakeContext))
            {
                var user = unitOfWork.Repository<User>()
                    .Query(x => x.UserName.Equals(username, StringComparison.OrdinalIgnoreCase))
                    .Select()
                    .FirstOrDefault();
                Assert.IsNotNull(user);
                string salt = password.GenerateSalt();
                string passwordHash = password.EncodeToHash(pwd1, salt);
                unitOfWork.Repository<User>().Insert(
                    new User
                    {
                        IsActive = false,
                        Password = pwd1,
                        PasswordSalt = salt,
                        PasswordHash = passwordHash,
                        ObjectState = ObjectState.Added
                    });

                passwordHash = password.EncodeToHash(pwd1, salt);
                unitOfWork.Repository<User>().Insert(
                    new User
                    {
                        //UserId = 2,
                        IsActive = true,
                        Password = pwd2,
                        PasswordSalt = salt,
                        PasswordHash = passwordHash,
                        ObjectState = ObjectState.Added
                    });

                passwordHash = password.EncodeToHash(pwd1, salt);
                unitOfWork.Repository<User>().Insert(
                    new User
                    {
                        //UserId = 3,
                        IsActive = true,
                        Password = pwd3,
                        PasswordSalt = salt,
                        PasswordHash = passwordHash,
                        ObjectState = ObjectState.Added
                    });

                //unitOfWork.SaveChanges();
                //var user = unitOfWork.Repository<User>().Find(userId);
                //Assert.IsNotNull(user);
                //Assert.AreEqual(userId, user.UserId);
            }
        }

        [Test(Description = "Insert list of Users")]
        [TestCase("Fake", "TestEight$1", "TestEight$2", "TestEight$3")]
        public void InsertRange(string searchName, string pwd1, string pwd2, string pwd3)
        {
            using (IDataContextAsync quickPromptFakeContext = new QuickPromptFakeContext())
            using (IUnitOfWorkAsync unitOfWork = new UnitOfWork(quickPromptFakeContext))
            {
                Password password = new Password();
                string salt = password.GenerateSalt();
                string passwordHash = password.EncodeToHash(pwd1, salt);
                var user1 = new User
                {
                    UserName = "hbopuri",
                    FirstName = "Harsha",
                    LastName = "Bopuri",
                    Locked = false,
                    IsDeleted = false,
                    IsAdmin = 1,
                    LastUpdatedUtc = DateTime.UtcNow,
                    DateCreatedUtc = DateTime.UtcNow,
                    UpdatedByUserId = 0,
                    IsActive = false,
                    Password = pwd1,
                    PasswordSalt = salt,
                    PasswordHash = passwordHash,
                    ObjectState = ObjectState.Added
                };

                passwordHash = password.EncodeToHash(pwd1, salt);
                var user2 = new User
                {
                    //UserGuid = Guid.NewGuid(),
                    UserName = "user2",
                    FirstName = "Two F Name",
                    LastName = "Last Name",
                    Locked = false,
                    IsDeleted = false,
                    IsAdmin = 1,
                    LastUpdatedUtc = DateTime.UtcNow,
                    DateCreatedUtc = DateTime.UtcNow,
                    UpdatedByUserId = 0,
                    IsActive = false,
                    Password = pwd1,
                    PasswordSalt = salt,
                    PasswordHash = passwordHash,
                    ObjectState = ObjectState.Added
                };

                var newUsers = new[]
                {
                    //new User {UserId = 1, IsActive = false, ObjectState = ObjectState.Added},
                    //new User {UserId = 2, IsActive = true, ObjectState = ObjectState.Added},
                    //new User {UserId = 3, IsActive = true, ObjectState = ObjectState.Added}
                    user1,user2
                };
                unitOfWork.Repository<User>().InsertRange(newUsers);
                var savedUsers = unitOfWork.Repository<User>().Query().Select();
                Assert.AreEqual(savedUsers.Count(), newUsers.Length);
            }
        }

        [Test(Description = "Update User")]
        [TestCase(2)]
        public void Update(int userId)
        {
            using (IDataContextAsync quickPromptFakeContext = new QuickPromptFakeContext())
            using (IUnitOfWorkAsync unitOfWork = new UnitOfWork(quickPromptFakeContext))
            {
                unitOfWork.Repository<User>().Insert(new User { IsActive = true, ObjectState = ObjectState.Added });
                unitOfWork.SaveChanges();
                var user = unitOfWork.Repository<User>().Find(2);
                Assert.AreEqual(user.IsActive, true, "Assert we are able to find the inserted user.");
                user.IsActive = false;
                user.ObjectState = ObjectState.Modified;
                unitOfWork.Repository<User>().Update(user);
                unitOfWork.SaveChanges();
                Assert.AreEqual(user.IsActive, false, "Assert that our changes were saved.");
            }
        }

        [Test(Description = "Find User Async")]
        [TestCase(2)]
        public async void FindAsync(int userId)
        {
            using (IDataContextAsync quickPromptFakeContext = new QuickPromptFakeContext())
            using (IUnitOfWorkAsync unitOfWork = new UnitOfWork(quickPromptFakeContext))
            {
                unitOfWork.Repository<User>().Insert(new User {IsActive = true });
                unitOfWork.SaveChanges();
                var user = await unitOfWork.RepositoryAsync<User>().FindAsync(2);
                //Assert.AreEqual(user.UserId, userId);
            }
        }
    }
}
